/**************************************************************************************************
  Filename:       epl_automation.c
  Revised:        $Date: 2012-12-10$
  Revision:       $Revision: 1 $ TONG KUN LAI

  Description:    This file contains the Simple GATT profile EPL GATT service 
                  profile for use with the BLE EPL automation application.

  Copyright 2012 EPL . All rights reserved.


**************************************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include "bcomdef.h"
#include "OSAL.h"
#include "linkdb.h"
#include "att.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "gattservapp.h"
#include "gapbondmgr.h"

#include "epl_automation.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

//#define SERVAPP_NUM_ATTR_SUPPORTED        17
#define SERVAPP_NUM_ATTR_SUPPORTED        35

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
// epl automation Profile Service UUID: 0xFFB0
CONST uint8 epl_automationServUUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_SERV_UUID), HI_UINT16(epl_automation_SERV_UUID)
};

// Characteristic 1 UUID: 0xFFB1
CONST uint8 epl_automationchar1UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR1_UUID), HI_UINT16(epl_automation_CHAR1_UUID)
};

// Characteristic 2 UUID: 0xFFB2
CONST uint8 epl_automationchar2UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR2_UUID), HI_UINT16(epl_automation_CHAR2_UUID)
};

// Characteristic 3 UUID: 0xFFB3
CONST uint8 epl_automationchar3UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR3_UUID), HI_UINT16(epl_automation_CHAR3_UUID)
};

// Characteristic 4 UUID: 0xFFB4
CONST uint8 epl_automationchar4UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR4_UUID), HI_UINT16(epl_automation_CHAR4_UUID)
};

// Characteristic 5 UUID: 0xFFF5
CONST uint8 epl_automationchar5UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR5_UUID), HI_UINT16(epl_automation_CHAR5_UUID)
};

// Characteristic 6 UUID: 0xFFB6
CONST uint8 epl_automationchar6UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR6_UUID), HI_UINT16(epl_automation_CHAR6_UUID)
};

// Characteristic 7 UUID: 0xFFB7
CONST uint8 epl_automationchar7UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR7_UUID), HI_UINT16(epl_automation_CHAR7_UUID)
};

// Characteristic 8 UUID: 0xFFB8
CONST uint8 epl_automationchar8UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR8_UUID), HI_UINT16(epl_automation_CHAR8_UUID)
};

// Characteristic 9 UUID: 0xFFB9
CONST uint8 epl_automationchar9UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR9_UUID), HI_UINT16(epl_automation_CHAR9_UUID)
};

// Characteristic 10 UUID: 0xFFBA
CONST uint8 epl_automationchar10UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR10_UUID), HI_UINT16(epl_automation_CHAR10_UUID)
};

// Characteristic 11 UUID: 0xFFBB
CONST uint8 epl_automationchar11UUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(epl_automation_CHAR11_UUID), HI_UINT16(epl_automation_CHAR11_UUID)
};

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

static epl_automationCBs_t *epl_automation_AppCBs = NULL;

/*********************************************************************
 * Profile Attributes - variables
 */

// Simple Profile Service attribute
static CONST gattAttrType_t epl_automationService = { ATT_BT_UUID_SIZE, epl_automationServUUID };




// Simple Profile Characteristic 1 Properties
static uint8 epl_automationChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 1 Value
static uint8 epl_automationChar1 = 99;
// Simple Profile Characteristic 1 User Description
static uint8 epl_automationChar1UserDesp[17] = "Characteristic 1\0";




// Simple Profile Characteristic 2 Properties
static uint8 epl_automationChar2Props = GATT_PROP_READ | GATT_PROP_READ;
// Characteristic 2 Value
static uint8 epl_automationChar2 = 0;
// Simple Profile Characteristic 2 User Description
static uint8 epl_automationChar2UserDesp[17] = "Characteristic 2\0";




// Simple Profile Characteristic 3 Properties
static uint8 epl_automationChar3Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 3 Value
static uint8 epl_automationChar3 = 0;
// Simple Profile Characteristic 3 User Description
static uint8 epl_automationChar3UserDesp[17] = "Characteristic 3\0";




// Simple Profile Characteristic 4 Properties
static uint8 epl_automationChar4Props = GATT_PROP_WRITE | GATT_PROP_READ | GATT_PROP_NOTIFY;
// Characteristic 4 Value
static uint8 epl_automationChar4 = 0;
// Simple Profile Characteristic 4 Configuration Each client has its own
// instantiation of the Client Characteristic Configuration. Reads of the
// Client Characteristic Configuration only shows the configuration for
// that client and writes only affect the configuration of that client.
static gattCharCfg_t epl_automationChar4Config[GATT_MAX_NUM_CONN];                                      
// Simple Profile Characteristic 4 User Description
static uint8 epl_automationChar4UserDesp[17] = "Characteristic 4\0";




// Simple Profile Characteristic 5 Properties
static uint8 epl_automationChar5Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 5 Value
//static uint8 epl_automationChar5[epl_automation_CHAR5_LEN] = { 0, 0, 0, 0, 0 };
static uint8 epl_automationChar5 = 0;
// Simple Profile Characteristic 5 User Description
static uint8 epl_automationChar5UserDesp[17] = "Characteristic 5\0";



// Simple Profile Characteristic 6 Properties
static uint8 epl_automationChar6Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 6 Value
static uint8 epl_automationChar6 = 0;
// Simple Profile Characteristic 6 User Description
static uint8 epl_automationChar6UserDesp[17] = "Characteristic 6\0";



// Simple Profile Characteristic 7 Properties
static uint8 epl_automationChar7Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 7 Value
static uint8 epl_automationChar7 = 0;
// Simple Profile Characteristic 7 User Description
static uint8 epl_automationChar7UserDesp[17] = "Characteristic 7\0";



// Simple Profile Characteristic 8 Properties
static uint8 epl_automationChar8Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 8 Value
static uint8 epl_automationChar8 = 0;
// Simple Profile Characteristic 8 User Description
static uint8 epl_automationChar8UserDesp[17] = "Characteristic 8\0";



// Simple Profile Characteristic 9 Properties
static uint8 epl_automationChar9Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 9 Value
static uint8 epl_automationChar9 = 0;
// Simple Profile Characteristic 9 User Description
static uint8 epl_automationChar9UserDesp[17] = "Characteristic 9\0";



// Simple Profile Characteristic 10 Properties
static uint8 epl_automationChar10Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 10 Value
static uint8 epl_automationChar10 = 0;
// Simple Profile Characteristic 10 User Description
static uint8 epl_automationChar10UserDesp[19] = "Characteristic 10\0";



// Simple Profile Characteristic 11 Properties
static uint8 epl_automationChar11Props = GATT_PROP_READ | GATT_PROP_WRITE;
// Characteristic 11 Value
static uint8 epl_automationChar11 = 0;
// Simple Profile Characteristic 11 User Description
static uint8 epl_automationChar11UserDesp[19] = "Characteristic 11\0";

/*********************************************************************
 * Profile Attributes - Table
 */

static gattAttribute_t epl_automationAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] = 
{
  // Simple Profile Service
  { 
    { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
    GATT_PERMIT_READ,                         /* permissions */
    0,                                        /* handle */
    (uint8 *)&epl_automationService            /* pValue */
  },

    // Characteristic 1 Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &epl_automationChar1Props 
    },

      // Characteristic Value 1
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar1UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar1 
      },

      // Characteristic 1 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar1UserDesp 
      },      

    // Characteristic 2 Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &epl_automationChar2Props 
    },

      // Characteristic Value 2
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar2UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar2 
      },

      // Characteristic 2 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar2UserDesp 
      },           
      
    // Characteristic 3 Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &epl_automationChar3Props 
    },

      // Characteristic Value 3
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar3UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar3 
      },

      // Characteristic 3 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar3UserDesp 
      },
    
    // Characteristic 4 Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &epl_automationChar4Props 
    },

      // Characteristic Value 4
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar4UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar4 
      },

      // Characteristic 4 configuration
      { 
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8 *)epl_automationChar4Config 
      },
      
      // Characteristic 4 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar4UserDesp 
      },
      
    // Characteristic 5 Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &epl_automationChar5Props 
    },

      // Characteristic Value 5
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar5UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar5 
      },

      // Characteristic 5 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar5UserDesp 
      },
      
      
      // Characteristic 6 Declaration
      { 
        { ATT_BT_UUID_SIZE, characterUUID },
        GATT_PERMIT_READ, 
        0,
        &epl_automationChar6Props 
      },
    
      // Characteristic Value 6
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar6UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar6 
      },

      // Characteristic 6 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar6UserDesp 
      },
      
      // Characteristic 7 Declaration
      { 
        { ATT_BT_UUID_SIZE, characterUUID },
        GATT_PERMIT_READ, 
        0,
        &epl_automationChar7Props 
      },
    
      // Characteristic Value 7
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar7UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar7 
      },

      // Characteristic 7 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar7UserDesp 
      },



      // Characteristic 8 Declaration
      { 
        { ATT_BT_UUID_SIZE, characterUUID },
        GATT_PERMIT_READ, 
        0,
        &epl_automationChar8Props 
      },
    
      // Characteristic Value 8
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar8UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar8 
      },

      // Characteristic 8 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar8UserDesp 
      },




      // Characteristic 9 Declaration
      { 
        { ATT_BT_UUID_SIZE, characterUUID },
        GATT_PERMIT_READ, 
        0,
        &epl_automationChar9Props 
      },
    
      // Characteristic Value 9
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar9UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar9 
      },

      // Characteristic 9 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar9UserDesp 
      },
      
      
      
      // Characteristic 10 Declaration
      { 
        { ATT_BT_UUID_SIZE, characterUUID },
        GATT_PERMIT_READ, 
        0,
        &epl_automationChar10Props 
      },
    
      // Characteristic Value 10
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar10UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar10 
      },

      // Characteristic 10User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar10UserDesp 
      },
      
      
      
      // Characteristic 11 Declaration
      { 
        { ATT_BT_UUID_SIZE, characterUUID },
        GATT_PERMIT_READ, 
        0,
        &epl_automationChar11Props 
      },
    
      // Characteristic Value 11
      { 
        { ATT_BT_UUID_SIZE, epl_automationchar11UUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        &epl_automationChar8 
      },

      // Characteristic 11 User Description
      { 
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ, 
        0, 
        epl_automationChar11UserDesp 
      },
};


/*********************************************************************
 * LOCAL FUNCTIONS
 */
static uint8 epl_automation_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, 
                            uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen );
static bStatus_t epl_automation_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint8 len, uint16 offset );
static void epl_automation_ProcessCharCfg( gattCharCfg_t *charCfgTbl, 
                                     uint8 *pValue, uint8 authenticated );
static gattCharCfg_t *epl_automation_FindCharCfgItem( uint16 connHandle, gattCharCfg_t *charCfgTbl );
static void epl_automation_ResetCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl );
static uint16 epl_automation_ReadCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl );
static uint8 epl_automation_WriteCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl, uint16 value );

static void epl_automation_HandleConnStatusCB( uint16 connHandle, uint8 changeType );




/*********************************************************************
 * NETWORK LAYER CALLBACKS
 */

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      epl_automation_AddService
 *
 * @brief   Initializes the Simple Profile service by registering
 *          GATT attributes with the GATT server.
 *
 * @param   services - services to add. This is a bit map and can
 *                     contain more than one service.
 *
 * @return  Success or Failure
 */
bStatus_t epl_automation_AddService( uint32 services )
{
  uint8 status = SUCCESS;

  // Initialize Client Characteristic Configuration attributes
  for ( uint8 i = 0; i < GATT_MAX_NUM_CONN; i++ )
  {
    epl_automationChar4Config[i].connHandle = INVALID_CONNHANDLE;
    epl_automationChar4Config[i].value = GATT_CFG_NO_OPERATION;

  }

  // Register with Link DB to receive link status change callback
  VOID linkDB_Register( epl_automation_HandleConnStatusCB );  
  
  if ( services & epl_automation_SERVICE )
  {
    // Register GATT attribute list and CBs with GATT Server App
    status = GATTServApp_RegisterService( epl_automationAttrTbl, GATT_NUM_ATTRS( epl_automationAttrTbl ),
                                          epl_automation_ReadAttrCB, epl_automation_WriteAttrCB, NULL );
  }

  return ( status );
}


/*********************************************************************
 * @fn      epl_automation_RegisterAppCBs
 *
 * @brief   Registers the application callback function. Only call 
 *          this function once.
 *
 * @param   callbacks - pointer to application callbacks.
 *
 * @return  SUCCESS or bleAlreadyInRequestedMode
 */
bStatus_t epl_automation_RegisterAppCBs( epl_automationCBs_t *appCallbacks )
{
  if ( appCallbacks )
  {
    epl_automation_AppCBs = appCallbacks;
    
    return ( SUCCESS );
  }
  else
  {
    return ( bleAlreadyInRequestedMode );
  }
}
  

/*********************************************************************
 * @fn      epl_automation_SetParameter
 *
 * @brief   Set a Simple Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   len - length of data to right
 * @param   value - pointer to data to write.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate 
 *          data type (example: data type of uint16 will be cast to 
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t epl_automation_SetParameter( uint8 param, uint8 len, void *value )
{
  bStatus_t ret = SUCCESS;
  switch ( param )
  {
    case epl_automation_CHAR1:
      if ( len == sizeof ( uint8 ) ) 
      {
        epl_automationChar1 = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case epl_automation_CHAR2:
      if ( len == sizeof ( uint8 ) ) 
      {
        epl_automationChar2 = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case epl_automation_CHAR3:
      if ( len == sizeof ( uint8 ) ) 
      {
        epl_automationChar3 = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case epl_automation_CHAR4:
      if ( len == sizeof ( uint8 ) ) 
      {
        epl_automationChar4 = *((uint8*)value);
        
        // See if Notification/Indication has been enabled
        //epl_automation_ProcessCharCfg( epl_automationChar4Config, &epl_automationChar4, FALSE );
        
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case epl_automation_CHAR5:
//      if ( len == epl_automation_CHAR5_LEN ) 
//      {
//        VOID osal_memcpy( epl_automationChar5, value, epl_automation_CHAR5_LEN );
//      }
      if( len == sizeof( uint8 ) )
      {
        epl_automationChar5 = *( (uint8*)value );
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;
      
      
      case epl_automation_CHAR6:
      if ( len == sizeof ( uint8 ) ) 
      {
        epl_automationChar6 = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;
      
      
      case epl_automation_CHAR7:
      if ( len == sizeof ( uint8 ) ) 
      {
        epl_automationChar7 = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;
      
      case epl_automation_CHAR8:
      if ( len == sizeof ( uint8 ) ) 
      {
        epl_automationChar8 = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;
      
    default:
      ret = INVALIDPARAMETER;
      break;
  }
  
  return ( ret );
}

/*********************************************************************
 * @fn      epl_automation_GetParameter
 *
 * @brief   Get a Simple Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   value - pointer to data to put.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate 
 *          data type (example: data type of uint16 will be cast to 
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t epl_automation_GetParameter( uint8 param, void *value )
{
  bStatus_t ret = SUCCESS;
  switch ( param )
  {
    case epl_automation_CHAR1:
      *((uint8*)value) = epl_automationChar1;
      break;

    case epl_automation_CHAR2:
      *((uint8*)value) = epl_automationChar2;
      break;      

    case epl_automation_CHAR3:
      *((uint8*)value) = epl_automationChar3;
      break;  

    case epl_automation_CHAR4:
      *((uint8*)value) = epl_automationChar4;
      break;

    case epl_automation_CHAR5:
      *((uint8*)value) = epl_automationChar5;
      //VOID osal_memcpy( value, epl_automationChar5, epl_automation_CHAR5_LEN );
      break;      
      
    case epl_automation_CHAR6:
      *((uint8*)value) = epl_automationChar6;
      break;
      
    case epl_automation_CHAR7:
      *((uint8*)value) = epl_automationChar7;
      break;
      
    case epl_automation_CHAR8:
      *((uint8*)value) = epl_automationChar8;
      break;
      
    default:
      ret = INVALIDPARAMETER;
      break;
  }
  
  return ( ret );
}

/*********************************************************************
 * @fn          epl_automation_ProcessCharCfg
 *
 * @brief       Process Client Charateristic Configuration change.
 *
 * @param       charCfgTbl - characteristic configuration table
 * @param       pValue - pointer to attribute value
 * @param       authenticated - whether an authenticated link is required
 *
 * @return      none
 */
static void epl_automation_ProcessCharCfg( gattCharCfg_t *charCfgTbl, 
                                     uint8 *pValue, uint8 authenticated )
{
  for ( uint8 i = 0; i < GATT_MAX_NUM_CONN; i++ )
  {
    gattCharCfg_t *pItem = &(charCfgTbl[i]);

    if ( ( pItem->connHandle != INVALID_CONNHANDLE ) &&
         ( pItem->value != GATT_CFG_NO_OPERATION ) )
    {
      gattAttribute_t *pAttr;
  
      // Find the characteristic value attribute 
      pAttr = GATTServApp_FindAttr( epl_automationAttrTbl, GATT_NUM_ATTRS( epl_automationAttrTbl ), pValue );
      if ( pAttr != NULL )
      {
        attHandleValueNoti_t noti;
    
        // If the attribute value is longer than (ATT_MTU - 3) octets, then
        // only the first (ATT_MTU - 3) octets of this attributes value can
        // be sent in a notification.
        if ( epl_automation_ReadAttrCB( pItem->connHandle, pAttr, noti.value,
                                  &noti.len, 0, (ATT_MTU_SIZE-3) ) == SUCCESS )
        {
          noti.handle = pAttr->handle;
  
          if ( pItem->value & GATT_CLIENT_CFG_NOTIFY )
          {
            VOID GATT_Notification( pItem->connHandle, &noti, authenticated );
          }
        }
      }
    }
  } // for
}

/*********************************************************************
 * @fn      epl_automation_FindCharCfgItem
 *
 * @brief   Find the characteristic configuration for a given client.
 *          Uses the connection handle to search the charactersitic 
 *          configuration table of a client.
 *
 * @param   connHandle - connection handle.
 * @param   charCfgTbl - characteristic configuration table.
 *
 * @return  pointer to the found item. NULL, otherwise.
 */
static gattCharCfg_t *epl_automation_FindCharCfgItem( uint16 connHandle, gattCharCfg_t *charCfgTbl )
{
  for ( uint8 i = 0; i < GATT_MAX_NUM_CONN; i++ )
  {
    if ( charCfgTbl[i].connHandle == connHandle )
    {
      // Entry found
      return ( &(charCfgTbl[i]) );
    }
  }

  return ( (gattCharCfg_t *)NULL );
}

/*********************************************************************
 * @fn      epl_automation_ResetCharCfg
 *
 * @brief   Reset the client characteristic configuration for a given
 *          client.
 *
 * @param   connHandle - connection handle.
 * @param   charCfgTbl - client characteristic configuration table.
 *
 * @return  none
 */
static void epl_automation_ResetCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl )
{
  gattCharCfg_t *pItem;

  pItem = epl_automation_FindCharCfgItem( connHandle, charCfgTbl );
  if ( pItem != NULL )
  {
    pItem->connHandle = INVALID_CONNHANDLE;
    pItem->value = GATT_CFG_NO_OPERATION;
  }
}

/*********************************************************************
 * @fn      epl_automation_ReadCharCfg
 *
 * @brief   Read the client characteristic configuration for a given
 *          client.
 *
 *          Note: Each client has its own instantiation of the Client
 *                Characteristic Configuration. Reads of the Client 
 *                Characteristic Configuration only shows the configuration
 *                for that client.
 *
 * @param   connHandle - connection handle.
 * @param   charCfgTbl - client characteristic configuration table.
 *
 * @return  attribute value
 */
static uint16 epl_automation_ReadCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl )
{
  gattCharCfg_t *pItem;

  pItem = epl_automation_FindCharCfgItem( connHandle, charCfgTbl );
  if ( pItem != NULL )
  {
    return ( (uint16)(pItem->value) );
  }

  return ( (uint16)GATT_CFG_NO_OPERATION );
}

/*********************************************************************
 * @fn      epl_automation_WriteCharCfg
 *
 * @brief   Write the client characteristic configuration for a given
 *          client.
 *
 *          Note: Each client has its own instantiation of the Client 
 *                Characteristic Configuration. Writes of the Client
 *                Characteristic Configuration only only affect the 
 *                configuration of that client.
 *
 * @param   connHandle - connection handle.
 * @param   charCfgTbl - client characteristic configuration table.
 * @param   value - attribute new value.
 *
 * @return  Success or Failure
 */
static uint8 epl_automation_WriteCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl,
                                    uint16 value )
{
  gattCharCfg_t *pItem;

  pItem = epl_automation_FindCharCfgItem( connHandle, charCfgTbl );
  if ( pItem == NULL )
  {
    pItem = epl_automation_FindCharCfgItem( INVALID_CONNHANDLE, charCfgTbl );
    if ( pItem == NULL )
    {
      return ( ATT_ERR_INSUFFICIENT_RESOURCES );
    }

    pItem->connHandle = connHandle;
  }

  // Write the new value for this client
  pItem->value = value;

  return ( SUCCESS );
}



/*********************************************************************
 * @fn          epl_automation_ReadAttrCB
 *
 * @brief       Read an attribute.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 *
 * @return      Success or Failure
 */
static uint8 epl_automation_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, 
                            uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen )
{
  bStatus_t status = SUCCESS;

  // If attribute permissions require authorization to read, return error
  if ( gattPermitAuthorRead( pAttr->permissions ) )
  {
    // Insufficient authorization
    return ( ATT_ERR_INSUFFICIENT_AUTHOR );
  }
  
  // Make sure it's not a blob operation (no attributes in the profile are long)
  if ( offset > 0 )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }
 
  if ( pAttr->type.len == ATT_BT_UUID_SIZE )
  {
    // 16-bit UUID
    uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
    switch ( uuid )
    {
      // No need for "GATT_SERVICE_UUID" case;
      // gattserverapp handles those reads
      
      case GATT_CLIENT_CHAR_CFG_UUID:
        {
          uint16 value = epl_automation_ReadCharCfg( connHandle, 
                                               (gattCharCfg_t *)(pAttr->pValue) );
          *pLen = 2;
          pValue[0] = LO_UINT16( value );
          pValue[1] = HI_UINT16( value );
        }
        break;

      // characteristics 1 and 2 have read permissions
      // characteritisc 3 does not have read permissions; therefore it is not
      //   included here
      // characteristic 4 does not have read permissions, but because it
      //   can be sent as a notification, it is included here
      case epl_automation_CHAR1_UUID:
      case epl_automation_CHAR2_UUID:
      case epl_automation_CHAR3_UUID:
      case epl_automation_CHAR4_UUID:
      case epl_automation_CHAR5_UUID:
      case epl_automation_CHAR6_UUID:
      case epl_automation_CHAR7_UUID:
      case epl_automation_CHAR8_UUID:
        *pLen = 1;
        pValue[0] = *pAttr->pValue;
        break;

//      case epl_automation_CHAR5_UUID:
//        *pLen = epl_automation_CHAR5_LEN;
//        VOID osal_memcpy( pValue, pAttr->pValue, epl_automation_CHAR5_LEN );
//        break;
        
      default:
        // Should never get here! (characteristics 3 and 4 do not have read permissions)
        *pLen = 0;
        status = ATT_ERR_ATTR_NOT_FOUND;
        break;
    }
  }
  else
  {
    // 128-bit UUID
    *pLen = 0;
    status = ATT_ERR_INVALID_HANDLE;
  }

  return ( status );
}

/*********************************************************************
 * @fn      epl_automation_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 * @param   complete - whether this is the last packet
 * @param   oper - whether to validate and/or write attribute value  
 *
 * @return  Success or Failure
 */
static bStatus_t epl_automation_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint8 len, uint16 offset )
{
  bStatus_t status = SUCCESS;
  uint8 notifyApp = 0xFF;
  
  // If attribute permissions require authorization to write, return error
  if ( gattPermitAuthorWrite( pAttr->permissions ) )
  {
    // Insufficient authorization
    return ( ATT_ERR_INSUFFICIENT_AUTHOR );
  }
  
  if ( pAttr->type.len == ATT_BT_UUID_SIZE )
  {
    // 16-bit UUID
    uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
    switch ( uuid )
    {
      case epl_automation_CHAR1_UUID:
      case epl_automation_CHAR2_UUID:
      case epl_automation_CHAR3_UUID:
      case epl_automation_CHAR4_UUID:
      case epl_automation_CHAR5_UUID:
      case epl_automation_CHAR6_UUID:
      case epl_automation_CHAR7_UUID:
      case epl_automation_CHAR8_UUID:

        //Validate the value
        // Make sure it's not a blob oper
        if ( offset == 0 )
        {
          if ( len != 1 )
          {
            status = ATT_ERR_INVALID_VALUE_SIZE;
          }
        }
        else
        {
          status = ATT_ERR_ATTR_NOT_LONG;
        }
        
        //Write the value
        if ( status == SUCCESS )
        {
          uint8 *pCurValue = (uint8 *)pAttr->pValue;        
          *pCurValue = pValue[0];

          if( pAttr->pValue == &epl_automationChar1 )
          {
            notifyApp = epl_automation_CHAR1;        
          }
          else
          {
            notifyApp = epl_automation_CHAR3;           
          }
        }
             
        break;

      case GATT_CLIENT_CHAR_CFG_UUID:
        // Validate the value
        // Make sure it's not a blob operation
        if ( offset == 0 )
        {
          if ( len == 2 )
          {
            uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] );
            
            // Validate characteristic configuration bit field
            if ( !( charCfg == GATT_CLIENT_CFG_NOTIFY   ||
                    charCfg == GATT_CFG_NO_OPERATION ) )
            {
              status = ATT_ERR_INVALID_VALUE;
            }
          }
          else
          {
            status = ATT_ERR_INVALID_VALUE_SIZE;
          }
        }
        else
        {
          status = ATT_ERR_ATTR_NOT_LONG;
        }
  
        // Write the value
        if ( status == SUCCESS )
        {
          uint16 value = BUILD_UINT16( pValue[0], pValue[1] );
          
          status = epl_automation_WriteCharCfg( connHandle,
                                          (gattCharCfg_t *)(pAttr->pValue),
                                          value );

          if ( status == SUCCESS )
          {
            // Update Bond Manager
            VOID GAPBondMgr_UpdateCharCfg( connHandle, pAttr->handle, value );
          }

        }
        break;
        
      default:
        // Should never get here! (characteristics 2 and 4 do not have write permissions)
        status = ATT_ERR_ATTR_NOT_FOUND;
        break;
    }
  }
  else
  {
    // 128-bit UUID
    status = ATT_ERR_INVALID_HANDLE;
  }

  // If a charactersitic value changed then callback function to notify application of change
 // if ( (notifyApp != 0xFF ) && epl_automation_AppCBs && epl_automation_AppCBs->pfnepl_automationChange )
  //{
    //epl_automation_AppCBs->pfnepl_automationChange( notifyApp );  
 //   epl_automation_AppCBs->pfnepl_automationChange(  );  
 // }
  
  return ( status );
}

/*********************************************************************
 * @fn          epl_automation_HandleConnStatusCB
 *
 * @brief       Simple Profile link status change handler function.
 *
 * @param       connHandle - connection handle
 * @param       changeType - type of change
 *
 * @return      none
 */
static void epl_automation_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
{ 
  // Make sure this is not loopback connection
  if ( connHandle != LOOPBACK_CONNHANDLE )
  {
    // Reset Client Char Config if connection has dropped
    if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && 
           ( !linkDB_Up( connHandle ) ) ) )
    { 
      epl_automation_ResetCharCfg( connHandle, epl_automationChar4Config );
    }
  }
}


/*********************************************************************
*********************************************************************/
